home *** CD-ROM | disk | FTP | other *** search
/ Aminet 37 / Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso / Aminet / util / cdity / DetotterButton.lha / DetotterButton / DetotterMouseButton.mod < prev    next >
Text File  |  2000-03-23  |  14KB  |  363 lines

  1. |##########|
  2. |#MAGIC   #|GNOLIEBE
  3. |#PROJECT #|"DetotterMouseButton"
  4. |#PATHS   #|"StdProject"
  5. |#LINK    #|"c:"
  6. |#GUIDE   #|""
  7. |#STACK   #|"4096"
  8. |#FLAGS   #|xx-x-x--xxx-xxx-x---------------
  9. |#USERSW  #|-x------------------------------
  10. |#USERMASK#|-x------------------------------
  11. |#SWITCHES#|xx---xxxxx-xx---
  12. |##########|
  13.  
  14. MODULE DetotterMouseButton;
  15.  
  16. (**************************************************************************
  17.  *
  18.  * DetotterMouseButton.mod
  19.  *   based on Swap_Buttons.c from the Amiga developer CD V1.1
  20.  *
  21.  * This detotters a button of an old mouse.
  22.  * The Cluster code installs and removes the input.device handler and
  23.  * manages the timer messages.
  24.  *
  25.  * The handler is written in assembly code since it is important that
  26.  * handlers be as fast as possible while processing the input events.
  27.  *
  28.  *)
  29.  
  30. $$IF Final THEN
  31.   $$RangeChk    := FALSE
  32.   $$OverflowChk := FALSE
  33.   $$ReturnChk   := FALSE
  34.   $$StrZeroChk  := FALSE
  35.   $$StackChk    := FALSE
  36.   $$NilChk      := FALSE
  37.   $$Debug       := FALSE
  38.   $$EntryCode   := FALSE
  39. $$END
  40.  
  41. FROM Input      IMPORT All;
  42. FROM System     IMPORT Regs;
  43. FROM Exec       IMPORT MsgGrp, InterruptPtr, SignalGrp, LibraryPtr;
  44.  
  45. TYPE
  46.   DetotterDataPtr = POINTER TO DetotterData;
  47.   DetotterData    =
  48.     RECORD
  49.       execBase       : LibraryPtr;    | Fast access to the ExecBase
  50.       delayedEvent   : InputEvent;    | Storage for the delayed release event
  51.       delayed        : BOOLEAN;       | Is delayedEvent valid?
  52.       qualifier      : Qualifiers;    | Qualifier bit for the processed mouse button
  53.       pressed        : QualifierSet;  | The qualifier bit is set if the virtual button is pressed
  54.       rawCode        : CARDINAL;      | Raw key code for the processed button
  55.       task           : TaskPtr;       | Main task
  56.       abortDelaySigSet,
  57.       delaySigSet    : TaskSigSet;    | Signals the main task listens to
  58.     END;
  59.  
  60. $$RangeChk    := FALSE
  61. $$OverflowChk := FALSE
  62. $$ReturnChk   := FALSE
  63. $$StrZeroChk  := FALSE
  64. $$StackChk    := FALSE
  65. $$NilChk      := FALSE
  66. $$Debug       := FALSE
  67. $$EntryCode   := FALSE
  68.  
  69. PROCEDURE Detotter (event IN A0 : InputEventPtr;
  70.                     data  IN A1 : DetotterDataPtr) : InputEventPtr;
  71. VAR
  72.   code      IN D2 : CARDINAL;
  73.   qualifier IN D3 : QualifierSet;
  74. |
  75. | The event list gets passed to you in a0.
  76. | The InputHandler.data field is passed to you in a1.
  77. |
  78. | On exit you must return the event list in d0.  In this way
  79. | you could add or remove items from the event list.
  80. |
  81. CONST
  82.   Signal  = -324;
  83.  
  84.   buttonMask      = CAST(QualifierSet,INTEGER(-1)) - QualifierSet:{leftButton, midButton, rightButton};
  85.  
  86.   | Compiler doesn't like this
  87.   |detotterDataPtr    = DetotterDataPtr(NIL);
  88.   |delayedEventOffset = detotterDataPtr.delayedEvent'PTR;
  89.   |delayedEventOffset = DetotterDataPtr(NIL).delayedEvent'ADR;
  90.  
  91. BEGIN
  92.   ASSEMBLE (
  93.     _Detotter:
  94.       move    event,-(a7)                     | Save the event list
  95.       move.l  d2-d3/a2,-(a7)                  | Save other registers
  96.  
  97. |
  98. | Since the event list could be a linked list, we start a loop
  99. | here to handle all of the events passed to us.
  100. |
  101.     CheckLoop:
  102. |
  103. | The actual button up/down events are transmitted as the
  104. | code field in RAWMOUSE events.  The code field must the be
  105. | checked and modified when needed on RAWMOUSE events.  If the
  106. | event is not a RAWMOUSE, we are done with it.
  107. |
  108.       move    event.qualifier,qualifier       | Get qualifier set ...
  109.       move    event.code,code                 | Get code ...
  110.       cmp     #Class.rawmouse,event.class     | Check for mouse
  111.       bne     FixQualifiers                   | If not, next...
  112.  
  113.       move    code,d0                         | Duplicate ...
  114.       bclr    #7,d0                           | Mask UP_PREFIX
  115.       cmp     data.rawCode,d0                 | Check for button
  116.       bne     FixQualifiers                   | No button to care about
  117.  
  118. |(* begin of debug block, if block is commented out, the thing should not crash
  119.  
  120.       | The button to detotter has changed
  121.       btst    #7,code                         | Is it pressed ?
  122.       beq     ButtonPressed
  123.  
  124.     ButtonReleased:                           | Button is physically released
  125.       lea     data.delayedEvent,a2
  126.       cmp     a2,event                        | Is this the message we delayed?
  127.       beq     PassDeferedRelease              | If yes, pass it without delaying again
  128.       tst     data.delayed                    | Is there a event delayed, already?
  129.       bne     RedeferRelease                  | Yes seems so
  130.  
  131.     DeferRelease:                             | A new release event occured
  132.       | InputEvent'SIZE = 22
  133.       move    event,-(a7)
  134.       |lea     data.delayedEvent,a2           | We have done this some lines before already
  135.       move.l  (event)+,(a2)+                  | Store this event
  136.       move.l  (event)+,(a2)+
  137.       move.l  (event)+,(a2)+
  138.       move.l  (event)+,(a2)+
  139.       move.l  (event)+,(a2)+
  140.       move.w  (event)+,(a2)+
  141.       move    (a7)+,event
  142.       move    #-1,data.delayed                | Mark this copy as valid
  143.  
  144.     RedeferRelease:
  145.       | There is already a deferred release event, flush the current only
  146.     SignalMainTask:                           | Tell the main task, that it has to start a timer
  147.       move.l  a0/a1/a6,-(a7)
  148.       move.l  data.execBase,a6
  149.       move.l  data.delaySigSet,d0
  150.       move.l  data.task,a1                    | Since data = a1 we have to write this last
  151.       jsr     Signal(a6)                      | Signal (main task, delay signal);
  152.       move.l  (a7)+,a0/a1/a6
  153.       bra     FlushMessage                    | Trash this message, since it is either stored or double
  154.  
  155.     PassDeferedRelease:                       | We shall pass this message without parsing
  156.       clr     data.delayed                    | This stops the delay state
  157.       clr     data.pressed                    | Let's clear the corresponding qualifier bit in our internal qualifier set
  158.       bra     NextEvent                       | This hasn't to be processed anymore, qualifiers are already correct
  159.  
  160.     ButtonPressed:
  161.       tst     data.delayed                    | Do we hold a delayed release event?
  162.       bne     AbortDelay                      | Yes, so we will flush this and the delayed event
  163.       move    data.qualifier,d0               | No, so we pass the button press event
  164.       clr.w   d1                              |   and refresh the internal qualifier set
  165.       bset    d0,d1                           |   according to this change
  166.       move    d1,data.pressed
  167.       bra     FixQualifiers
  168.  
  169.     AbortDelay:
  170.       clr     data.delayed                    | This stops the delay state, too
  171.       move.l  a0/a1/a6,-(a7)
  172.       move.l  data.execBase,a6
  173.       move.l  data.abortDelaySigSet,d0
  174.       move.l  data.task,a1                    | Since data = a1 we have to write this last
  175.       jsr     Signal(a6)                      | Signal (main task, delay abort signal);
  176.       move.l  (a7)+,a0/a1/a6
  177.  
  178.     FlushMessage:
  179.       move    #Input.noButton,code            | Setting code to noButton turns this message into a dummy event
  180.       bra     StoreEvent                      | This hasn't to be processed anymore, qualifiers are already correct
  181. | end of block *)
  182. |
  183. | Since we are changing mouse button events, we need to make
  184. | sure that we change the qualifiers on all of the messages.  The
  185. | mouse buttons are tracked in the message qualifiers
  186. | for use in such things as dragging.  To make sure that we continue
  187. | to drag correctly, we change the qualifiers.
  188. |
  189.     FixQualifiers:
  190.       move    data.qualifier,d0
  191.       bclr    d0,qualifier
  192.       or      data.pressed,qualifier          | Set the processed qualifier to the emulated state
  193.  
  194.     StoreEvent:
  195.       move    code,event.code                 | Save back...
  196.       move    qualifier,event.qualifier
  197.       |move.l #$123134,0  | provoke Enforcer
  198. |
  199. | The event list is linked via a pointer to the next event
  200. | in the first element of the structure.  That is why it is not
  201. | nessesary to use:  move.l ie_NextEvent(a0),d0
  202. |
  203. | The reason I move to d0 first is that this also checks for zero.
  204. | The last event in the list will have a NULL ie_NextEvent field.
  205. | This is NOT as standard EXEC list where the node after the last
  206. | node is NULL.  Input events are single-linked for performance.
  207. |
  208.     NextEvent:
  209.       move    event.nextEvent,d0              | Get next event, check = NIL
  210.       move    d0,event                        | Into a0 ...
  211.       bne     CheckLoop                       | Do some more.
  212. |
  213. | All done, just return the event list...  (in d0)
  214. |
  215.       move.l  (a7)+,d2-d3/a2                  | Restore other registers
  216.       move.l  (a7)+,d0                        | Get event list back...
  217.       rts                                     | Return from handler...
  218.   );
  219. END Detotter;
  220.  
  221. $$RangeChk    := OLD
  222. $$OverflowChk := OLD
  223. $$ReturnChk   := OLD
  224. $$StrZeroChk  := OLD
  225. $$StackChk    := OLD
  226. $$NilChk      := OLD
  227. $$Debug       := OLD
  228.  
  229. FROM Resources  IMPORT New, MemReqs;
  230. FROM T_Exec     IMPORT NoFreeSignal;
  231. FROM Timer      IMPORT All;
  232.  
  233. VAR
  234.   inputReqBlk       : IOInputPtr;
  235.   releaseTimer      : IOTimerPtr;
  236.   inputHandler      : InterruptPtr;
  237.   abortDelaySignal,
  238.   delaySignal       : TaskSignals;
  239.   timerSigSet       := TaskSigSet:{};
  240.   recSigSet         : TaskSigSet;
  241.   waitSigSet        := TaskSigSet:{ctrlC};
  242.  
  243. CONST
  244.   DefaultValues = ARRAY OF LONGINT : (0, 20, 100);
  245.  
  246. FROM Dos        IMPORT VPrintf;
  247. FROM CLIStartup IMPORT All;
  248.  
  249. TYPE
  250.   LongPtr  = POINTER TO LONGINT;
  251.   ArgRec   = RECORD
  252.                button : LongPtr;
  253.                delay  : LongPtr;
  254.                pri    : LongPtr;
  255.              END;
  256.  
  257. VAR
  258.   args    := ArgRec : (DefaultValues[0]'PTR, DefaultValues[1]'PTR, DefaultValues[2]'PTR);
  259.  
  260. BEGIN
  261.   ReadArgs ("BUTTON/N,DELAY/N,PRIORITY=PRI/N", args);
  262.   IF args.button^ NOT OF 0..2 THEN
  263.     FORGET VPrintf ("BUTTON must be of 0..2."+&10);
  264.     RAISE (ScriptError);
  265.   END;
  266.   IF args.delay^ <= 0 THEN
  267.     FORGET VPrintf ("DELAY must be positive."+&10);
  268.     RAISE (ScriptError);
  269.   END;
  270.   IF args.pri^ NOT OF SHORTINT'MIN..SHORTINT'MAX THEN
  271.     FORGET VPrintf ("PRIORITY must be of -128..127."+&10);
  272.     RAISE (ScriptError);
  273.   END;
  274.  
  275.   New (inputHandler, mem := {public, clear});
  276.   inputHandler.code := PROC (Detotter);
  277.   inputHandler.pri  := args.pri^;
  278.   inputHandler.name := "Detotter mouse button";
  279.  
  280.   WITH DetotterDataPtr (inputHandler.data) AS data DO
  281.     New (data, mem := {public, clear});
  282.          delaySignal := AllocSignal(anySignal); ASSERT (#, NoFreeSignal);
  283.     abortDelaySignal := AllocSignal(anySignal); ASSERT (#, NoFreeSignal);
  284.     INCL (data.     delaySigSet,      delaySignal); INCL (waitSigSet,      delaySignal);
  285.     INCL (data.abortDelaySigSet, abortDelaySignal); INCL (waitSigSet, abortDelaySignal);
  286.     data.task      := TaskPtr (System.OwnTask);
  287.     data.execBase  := Exec.ExecBase;
  288.     data.qualifier := leftButton;    DEC (data.qualifier, args.button^);
  289.     data.rawCode   := Input.lButton; INC (data.rawCode,   args.button^);
  290.   END;
  291.  
  292.   releaseTimer := OpenTimer (microHz);
  293.   INCL (waitSigSet,  releaseTimer.replyPort.sigBit);
  294.   INCL (timerSigSet, releaseTimer.replyPort.sigBit);
  295.  
  296.   inputReqBlk := OpenInput();
  297.   inputReqBlk.data    := inputHandler;
  298.   inputReqBlk.command := Input.addHandler;
  299.   FORGET DoIO (inputReqBlk);
  300.  
  301.   REPEAT
  302.     recSigSet := Wait (waitSigSet);
  303.     |WriteFormat ("signals %8lx received"+&10, data := CAST(LONGINT, recSigSet));
  304.     WITH DetotterDataPtr (inputHandler.data) AS data DO
  305.       |WriteFormat ("delayed event(%ld), interim check, rawCode %ld, qualifiers %4lx"+&10,
  306.         |data := CAST(SHORTINT,data.delayed), data.delayedEvent.code, CAST (CARDINAL, data.delayedEvent.qualifier));
  307.  
  308.       (* Delay signal and abort delay signal may arrive at the same time,
  309.          and you will not know, which event was the first.
  310.          It depends on the order of the following two if statements,
  311.          whether mouse will emulated as pressed or as released in this case.
  312.       *)
  313.       IF delaySignal IN recSigSet THEN
  314.         |WriteString ("delay release"+&10);
  315.         IF CheckIO (releaseTimer)=NIL THEN
  316.           AbortIO (releaseTimer);
  317.           | do not WaitIO() here, this will lock the program
  318.         END;
  319.  
  320.         | it's important to set this here, you cannot re-use this values
  321.         releaseTimer.command    := addRequest;
  322.         releaseTimer.time.secs  :=  args.delay^ DIV 1000;
  323.         releaseTimer.time.micro := (args.delay^ MOD 1000) * 1000;
  324.         SendIO (releaseTimer);
  325.       END;
  326.       IF abortDelaySignal IN recSigSet THEN
  327.         |WriteString ("abort delay"+&10);
  328.         IF CheckIO (releaseTimer)=NIL THEN
  329.           AbortIO (releaseTimer);
  330.           | make sure, that the final release caused by a returning timer message is supressed
  331.           FORGET WaitIO (releaseTimer);
  332.           FORGET SetSignal ({}, timerSigSet);
  333.         END;
  334.       END;
  335.       IF releaseTimer.replyPort.sigBit IN recSigSet THEN
  336.         |WriteString ("finally release"+&10);
  337.         FORGET WaitIO (releaseTimer);
  338.         FORGET SetSignal ({}, timerSigSet);
  339.         inputReqBlk.data    := data.delayedEvent'PTR;
  340.         inputReqBlk.length  := InputEvent'SIZE;
  341.         inputReqBlk.command := Input.writeEvent;
  342.         FORGET DoIO (inputReqBlk);
  343.       END;
  344.     END;
  345.   UNTIL ctrlC IN recSigSet;  | unnecessary, Cluster installs a SoftException for CtrlC
  346.  
  347. CLOSE
  348.   IF inputReqBlk#NIL THEN
  349.     inputReqBlk.data    := inputHandler;
  350.     inputReqBlk.command := Input.remHandler;
  351.     FORGET DoIO (inputReqBlk);
  352.   END;
  353.  
  354.   IF      delaySignal # noSignal THEN FreeSignal (     delaySignal) END;
  355.   IF abortDelaySignal # noSignal THEN FreeSignal (abortDelaySignal) END;
  356.   IF CheckIO (releaseTimer)=NIL THEN AbortIO (releaseTimer) END;
  357.   | according to AutoDocs WaitIO _is_ _necessary_,
  358.   | but if the Request wasn't used ever, this blocks the cleanup :-(
  359.   |FORGET WaitIO (releaseTimer);
  360.   |FORGET GetMsg (releaseTimer.replyPort);
  361. END DetotterMouseButton.
  362.  
  363.